<!-- 一个面板，处理翻页逻辑、占位符的自动显示与否 -->
<template>
  <div :class="css.component" @scroll.passive="onScroll">
    <slot v-if="confirmNoData" name="placeholder">
      <r-placeholder/>
    </slot>
    <template v-else>
      <div :class="css.content">
        <slot/>
        <div v-if="loading" :class="css.loading">
          <r-icon name="loading" class="animation-rotate"/>
          <span>加载中</span>
        </div>
      </div>
      <template v-if="this.list !== null && !loading">
        <p v-if="!hasNextPage" :class="css.bar">
          <a @click="loadPrevPage" :class="css.link" v-if="hasPrevPage && replace">上一页</a>
          <span :class="css.tips">已经到底啦～</span>
        </p>
        <template v-else>
          <p v-if="replace" :class="css.bar">
            <a @click="loadPrevPage" :class="css.link" v-if="hasPrevPage">上一页</a>
            <a @click="loadNextPage" :class="css.link">下一页</a>
          </p>
          <a v-else @click="loadNextPage" :class="css.link">加载更多</a>
        </template>
      </template>
    </template>
  </div>
</template>
<script>
import RPlaceholder from './r-placeholder'
import RIcon from './r-icon'
import isArray from 'src/utils/isArray'
const defaultValues = {
  ps: {
    key: 'ps',
    value: 10
  },
  pn: {
    key: 'pn',
    value: 1
  }
}
export default {
  name: 'r-panel',
  model: {
    event: 'update',
    prop: 'list'
  },
  components: {
    RPlaceholder,
    RIcon
  },
  props: {
    /**
     * @prop bluebird 由父组件组装进当前组件的一个函数
     * 该函数接收一个pageInfo对象(pageInfo对象里有页码，页值信息)
     * 该函数返回一个list数组，或者一个resolve值为list数组的promise
     */
    bluebird: {
      type: Function,
      required: true
    },
    /**
     * 是否跳过首次加载
     * 有时首次加载可能是有条件的，比如需要等到某些接口数据加载完成。所以这里允许跳过首次加载，让父组件可以更灵活地处理
     */
    skipFirst: {
      type: Boolean
    },
    /**
     * 计算面板状态需要当前list
     */
    list: {
      type: Array
    },
    /**
     * 翻页长度所用的key和value，也可以只传key或者value
     */
    ps: {
      type: Object
    },
    /**
     * 翻页位置所用的key和value，也可以只传key或者value
     */
    pn: {
      type: Object
    },
    /**
     * 是否将翻页信息关联路由
     * 如果关联路由，翻页数据变动时会更新路由，同时会使用路由初始化翻页信息
     */
    syncRoute: {
      type: Boolean
    },
    /**
     * 加载新数据时采取替换模式(如果replace为true的话)
     */
    replace: {
      type: Boolean
    }
  },
  data () {
    const fullPs = {...defaultValues.ps, ...this.ps}
    const fullPn = {...defaultValues.pn, ...this.pn}
    fullPs.defaultValue = fullPs.value
    fullPn.defaultValue = fullPn.value
    if (this.syncRoute) {
      const {query} = this.$route
      let pageSize = query[fullPs.key]
      let pageNumber = query[fullPn.key]
      if (pageSize != null && pageSize !== '') {
        fullPs.value = parseInt(pageSize) || fullPs.defaultValue
      }
      if (pageNumber != null && pageNumber !== '') {
        fullPn.value = parseInt(pageNumber) || fullPn.defaultValue
      }
    }
    return {
      fullPs,
      fullPn,
      hasNextPage: true,
      loading: false
    }
  },
  computed: {
    pageSize: {
      get () {
        return this.fullPs.value
      },
      set (pageSize) {
        this.fullPs.value = pageSize
        if (this.syncRoute) {
          const {fullPs: ps} = this
          const query = {...this.$route.query, [ps.key]: pageSize}
          if (pageSize === ps.defaultValue) {
            delete query[ps.key]
          }
          this.$router.replace({query})
        }
      }
    },
    pageNumber: {
      get () {
        return this.fullPn.value
      },
      set (pageNumber) {
        this.fullPn.value = pageNumber
        if (this.syncRoute) {
          const {fullPn: pn} = this
          const query = {...this.$route.query, [pn.key]: pageNumber}
          if (pageNumber === pn.defaultValue) {
            delete query[pn.key]
          }
          this.$router.replace({query})
        }
      }
    },
    hasPrevPage () {
      const {value, defaultValue} = this.fullPn
      return value > defaultValue
    },
    confirmNoData () {
      return isArray(this.list) && this.list.length === 0
    }
  },
  created () {
    // 向外暴露一个刷新器，可用该刷新器来刷新(重置)面板的页数和每页加载大小的数据
    this.$emit('connect', () => this.refresh())
    if (!this.skipFirst) {
      this.syncRoute ? this.refresh() : this.loadStartPage()
    }
  },
  methods: {
    /**
     * 清空页码，重置为默认页，并加载默认页
     */
    refresh () {
      const {fullPs: ps, fullPn: pn} = this
      this.pageSize = ps.defaultValue
      this.pageNumber = pn.defaultValue
      this.loadStartPage()
    },
    /**
     * 加载初始页
     */
    loadStartPage () {
      const {fullPs: ps, fullPn: pn} = this
      const pageInfo = {
        [ps.key]: ps.value,
        [pn.key]: pn.value
      }
      this.load(pageInfo).then(list => {
        list = list || []
        this.hasNextPage = list.length >= this.fullPs.value
        this.$emit('update', list)
      }).catch(console.error).finally(this.end)
    },
    onScroll (event) {
      // TODO: 下拉刷新
      // TODO: 滚动加载
    },
    /**
     * 加载上一页
     * 共有如下几件事需要完成：
     * 1. 为列表设置默认值 []
     * 2. 将页数-1
     * 3. 更新数据加载是否结束并更新相应状态(一定还没有结束)
     * 4. emit事件更新props
     */
    loadPrevPage () {
      const {fullPs: ps, fullPn: pn} = this
      const pageInfo = {
        [ps.key]: ps.value,
        [pn.key]: pn.value - 1
      }
      this.load(pageInfo).then(list => {
        list = list || []
        this.pageNumber = pn.value - 1
        this.hasNextPage = true
        this.$emit('update', list)
      }).catch(console.error).finally(this.end)
    },
    /**
     * 加载下一页
     * 共有如下几件事需要完成：
     * 1. 为列表设置默认值 []
     * 2. 将页数+1，如果有数据的话
     * 3. 判断数据加载是否结束并更新相应状态
     * 4. emit事件更新props
     */
    loadNextPage () {
      const replace = this.replace
      const {fullPs: ps, fullPn: pn} = this
      const pageInfo = {
        [ps.key]: ps.value,
        [pn.key]: pn.value + 1
      }
      this.load(pageInfo, {replace}).then(list => {
        list = list || []
        if (list.length > 0) {
          this.pageNumber++
        }
        this.hasNextPage = list.length >= this.fullPs.value
        this.$emit('update', replace ? list : this.list.concat(list))
      }).catch(console.error).finally(this.end)
    },
    load (pageInfo, config = {}) {
      // 如果新获取到数据需要替换的话，(加载前会)把现有数据清空
      if (config.replace) {
        this.$emit('update', null)
      }
      this.loading = true
      return Promise.resolve(this.bluebird(pageInfo, config))
    },
    end () {
      this.loading = false
    }
  }
}
</script>
<style module="css" lang="scss">
  @import "../styles/index";

  .component {
    @extend %flex-start;
    flex-direction: column;
  }

  .content {
    flex: 1;
    align-self: stretch;
  }

  .bar {
    @extend %flex-center;
    min-height: 40px;
    align-self: stretch;
  }

  .tips {
    color: #999;
  }

  .link {
    color: $color-primary;
  }

  .tips,
  .link {
    font-size: 13px;
    text-align: center;
    line-height: 30px;
    margin: 5px 0;
    padding: 0 10px;
  }

  .link + .link {
    @extend %left-before-thin;

    &::before {
      top: 25%;
      height: 50%;
    }
  }

  .loading {
    @extend %flex-center;
    flex-direction: column;
    color: $color-primary;
    font-size: 12px;
    margin: 10px 0;

    svg {
      fill: currentColor;
      width: 20px;
      margin: 2px;
    }
  }
</style>
